
package lex;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;

import common.Enum;
import common.Enum.lexType;
import common.Error;
import common.Node;

/**
 * 词法分析器
 * 
 * @author Chong
 *
 */
public class Scanner {

	static int line, // 行号
			row,// 列号
			cur;// 当前字符下标
	 static String Buffer;// 缓冲区
	public static Enum.stateType[][] T; // 状态转换矩阵
	static Enum.stateType curState;

	/**
	 * 构造函数
	 */
	public Scanner() {
		line = 1;
		row = 1;
		cur = 0;
		curState = Enum.stateType.S0;

		T = new Enum.stateType[][] {
				{ Enum.stateType.S1, Enum.stateType.S2, Enum.stateType.S4,
						Enum.stateType.S6, Enum.stateType.S7,
						Enum.stateType.S9, Enum.stateType.S3,
						Enum.stateType.S3, Enum.stateType.S3,
						Enum.stateType.S3, Enum.stateType.S3,
						Enum.stateType.S3, Enum.stateType.S3,
						Enum.stateType.S3, Enum.stateType.S3,
						Enum.stateType.S3, Enum.stateType.S3,
						Enum.stateType.S3, Enum.stateType.S3, Enum.stateType.S3 },
				{ Enum.stateType.S1, Enum.stateType.S1, Enum.stateType.ID,
						Enum.stateType.ID, Enum.stateType.ID,
						Enum.stateType.ID, Enum.stateType.ID,
						Enum.stateType.ID, Enum.stateType.ID,
						Enum.stateType.ID, Enum.stateType.ID,
						Enum.stateType.ID, Enum.stateType.ID,
						Enum.stateType.ID, Enum.stateType.ID,
						Enum.stateType.ID, Enum.stateType.ID,
						Enum.stateType.ID, Enum.stateType.ID, Enum.stateType.ID },
				{ Enum.stateType.INTC, Enum.stateType.S2, Enum.stateType.INTC,
						Enum.stateType.INTC, Enum.stateType.INTC,
						Enum.stateType.INTC, Enum.stateType.INTC,
						Enum.stateType.INTC, Enum.stateType.INTC,
						Enum.stateType.INTC, Enum.stateType.INTC,
						Enum.stateType.INTC, Enum.stateType.INTC,
						Enum.stateType.INTC, Enum.stateType.INTC,
						Enum.stateType.INTC, Enum.stateType.INTC,
						Enum.stateType.INTC, Enum.stateType.INTC,
						Enum.stateType.INTC },
				{ Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL },
				{ Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.S5, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR },
				{ Enum.stateType.ASSIGN, Enum.stateType.ASSIGN,
						Enum.stateType.ASSIGN, Enum.stateType.ASSIGN,
						Enum.stateType.ASSIGN, Enum.stateType.ASSIGN,
						Enum.stateType.ASSIGN, Enum.stateType.ASSIGN,
						Enum.stateType.ASSIGN, Enum.stateType.ASSIGN,
						Enum.stateType.ASSIGN, Enum.stateType.ASSIGN,
						Enum.stateType.ASSIGN, Enum.stateType.ASSIGN,
						Enum.stateType.ASSIGN, Enum.stateType.ASSIGN,
						Enum.stateType.ASSIGN, Enum.stateType.ASSIGN,
						Enum.stateType.ASSIGN, Enum.stateType.ASSIGN },
				{ Enum.stateType.INCOMMENT, Enum.stateType.INCOMMENT,
						Enum.stateType.INCOMMENT, Enum.stateType.INCOMMENT,
						Enum.stateType.INCOMMENT, Enum.stateType.INCOMMENT,
						Enum.stateType.INCOMMENT, Enum.stateType.INCOMMENT,
						Enum.stateType.INCOMMENT, Enum.stateType.INCOMMENT,
						Enum.stateType.INCOMMENT, Enum.stateType.INCOMMENT,
						Enum.stateType.INCOMMENT, Enum.stateType.INCOMMENT,
						Enum.stateType.INCOMMENT, Enum.stateType.INCOMMENT,
						Enum.stateType.INCOMMENT, Enum.stateType.INCOMMENT,
						Enum.stateType.INCOMMENT, Enum.stateType.INCOMMENT },
				{ Enum.stateType.SPECIAL, Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.S8,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL, Enum.stateType.SPECIAL,
						Enum.stateType.SPECIAL },
				{ Enum.stateType.INRANGE, Enum.stateType.INRANGE,
						Enum.stateType.INRANGE, Enum.stateType.INRANGE,
						Enum.stateType.INRANGE, Enum.stateType.INRANGE,
						Enum.stateType.INRANGE, Enum.stateType.INRANGE,
						Enum.stateType.INRANGE, Enum.stateType.INRANGE,
						Enum.stateType.INRANGE, Enum.stateType.INRANGE,
						Enum.stateType.INRANGE, Enum.stateType.INRANGE,
						Enum.stateType.INRANGE, Enum.stateType.INRANGE,
						Enum.stateType.INRANGE, Enum.stateType.INRANGE,
						Enum.stateType.INRANGE, Enum.stateType.INRANGE },
				{ Enum.stateType.S10, Enum.stateType.S10, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR },
				{ Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.S11,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR },
				{ Enum.stateType.CHAR, Enum.stateType.CHAR,
						Enum.stateType.CHAR, Enum.stateType.CHAR,
						Enum.stateType.CHAR, Enum.stateType.CHAR,
						Enum.stateType.CHAR, Enum.stateType.CHAR,
						Enum.stateType.CHAR, Enum.stateType.CHAR,
						Enum.stateType.CHAR, Enum.stateType.CHAR,
						Enum.stateType.CHAR, Enum.stateType.CHAR,
						Enum.stateType.CHAR, Enum.stateType.CHAR,
						Enum.stateType.CHAR, Enum.stateType.CHAR,
						Enum.stateType.CHAR, Enum.stateType.CHAR },
				{ Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR,
						Enum.stateType.ERROR, Enum.stateType.ERROR }

		}; // 状态转换矩阵

	}

	/**
	 * 传入文件地址(string)，读入源文件，返回string 功能是将源文件读入Buffer中
	 * 
	 * @param filePath
	 * @return
	 * @throws Exception
	 */
	public static String readTxt(String filePath) throws Exception {
		FileReader file = new FileReader(filePath);
		BufferedReader reader = new BufferedReader(file);
		String temp = "";
		while (reader.ready()) {
			temp += reader.readLine();
			temp += '\n';
		}
		// 关闭reader
		reader.close();
		return temp;
	}

	/**
	 * 取得下一个非空字符，并将该字符的下标赋值给cur 如果到了文件结束返回'\0'
	 * 
	 * @return
	 */
	public static char getNextChar() {
		int i;
		char ch = '\0';
		// 如果当前字符是缓冲区最后一个字符返回'\0'
		if (cur == Buffer.length() - 1) {
			ch = '\0';
			return ch;
		}
		for (i = cur; i < Buffer.length(); i++) {
			// 如果当前字符为回车,line+1,row=1
			if (Buffer.charAt(i) == '\n') {
				line++;
				row = 1;
			} else if (Buffer.charAt(i) == ' ') {
				// 如果当前字符为空格,row+1
				row++;
			} else if (Buffer.charAt(i) == '\t') {
				// 如果当前字符为tab,row+4
				row += 4;
			} else
				break;
		}
		if (i != Buffer.length()) {
			// 未处理完毕
			ch = Buffer.charAt(i);
		} else {
			ch = '\0';
		}
		cur = i;
		return ch;
	}

	/**
	 * 识别数字, 是则返回字符串, 否则返回null
	 * 
	 * @param ch
	 * @return
	 */
	public static String isNumber(char ch) {
		String res = "";
		int temp = cur;
		while (Buffer.charAt(temp) >= '0' && Buffer.charAt(temp) <= '9') {
			res += Buffer.charAt(temp);
			temp++;
			row++;
		}
		if ((Buffer.charAt(temp) >= 'a' && Buffer.charAt(temp) <= 'z')
				|| (Buffer.charAt(temp) >= 'A' && Buffer.charAt(temp) <= 'Z'))
			res = null;
		cur = temp;
		return res;
	}

	/**
	 * 识别标示符或者保留字
	 * 
	 * @param ch
	 * @return
	 */
	public static String isName(char ch) {
		String res = "";
		int temp = cur;
		while ((Buffer.charAt(temp) >= '0' && Buffer.charAt(temp) <= '9')
				|| (Buffer.charAt(temp) >= 'a' && Buffer.charAt(temp) <= 'z')
				|| (Buffer.charAt(temp) >= 'A' && Buffer.charAt(temp) <= 'Z')) {
			res += Buffer.charAt(temp);
			temp++;
			row++;
		}
		cur = temp;
		return res;
	}

	/**
	 * 识别具体是哪个保留字或者为ID标识符
	 * 
	 * @param name
	 * @return
	 */
	public static lexType recognizeName(String name) {
		// jdk1.7以上可以使用
		switch (name) {
		case "program":
			return Enum.lexType.PROGRAM;
		case "type":
			return Enum.lexType.TYPE;
		case "var":
			return Enum.lexType.VAR;
		case "procedure":
			return Enum.lexType.PROCEDURE;
		case "begin":
			return Enum.lexType.BEGIN;
		case "end":
			return Enum.lexType.END;
		case "array":
			return Enum.lexType.ARRAY;
		case "of":
			return Enum.lexType.OF;
		case "record":
			return Enum.lexType.RECORD;
		case "if":
			return Enum.lexType.IF;
		case "then":
			return Enum.lexType.THEN;
		case "else":
			return Enum.lexType.ELSE;
		case "fi":
			return Enum.lexType.FI;
		case "while":
			return Enum.lexType.WHILE;
		case "do":
			return Enum.lexType.DO;
		case "endwh":
			return Enum.lexType.ENDWH;
		case "read":
			return Enum.lexType.READ;
		case "write":
			return Enum.lexType.WRITE;
		case "return":
			return Enum.lexType.RETURN;
		case "integer":
			return Enum.lexType.INTEGER;
		case "char":
			return Enum.lexType.CHAR;
		default:
			return Enum.lexType.ID;
		}
	}

	/**
	 * 识别该字符具体是哪种殊符号
	 * 
	 * @param symbol
	 * @return
	 */
	public static lexType recognizeSymbol(char symbol) {
		switch (symbol) {
		case '+':
			return Enum.lexType.PLUS;
		case '-':
			return Enum.lexType.MINUS;
		case '*':
			return Enum.lexType.TIMES;
		case '/':
			return Enum.lexType.OVER;
		case '(':
			return Enum.lexType.LPAREN;
		case ')':
			return Enum.lexType.RPAREN;
		case '.':
			return Enum.lexType.DOT;
		case '[':
			return Enum.lexType.LMIDPAREN;
		case ']':
			return Enum.lexType.RMIDPAREN;
		case ';':
			return Enum.lexType.SEMI;
		case ':':
			return Enum.lexType.COLON;
		case ',':
			return Enum.lexType.COMMA;
		case '<':
			return Enum.lexType.LT;
		case '=':
			return Enum.lexType.EQ;
		case '\'':
			return Enum.lexType.CHARC;
		case '\0':
			return Enum.lexType.ENDFILE;
		}
		return null;
	}

	/**
	 * 识别该字符具体是哪种符号(charType)
	 * 
	 * @param symbol
	 * @return charType
	 */
	public static Enum.charType recognizeChar(char symbol) {
		switch (symbol) {
		case '+':
			return Enum.charType.PLUS;
		case '-':
			return Enum.charType.MINUS;
		case '*':
			return Enum.charType.TIMES;
		case '/':
			return Enum.charType.OVER;
		case '(':
			return Enum.charType.LPAREN;
		case ')':
			return Enum.charType.RPAREN;
		case '.':
			return Enum.charType.DOT;
		case '[':
			return Enum.charType.LMIDPAREN;
		case ']':
			return Enum.charType.RMIDPAREN;
		case ';':
			return Enum.charType.SEMI;
		case ':':
			return Enum.charType.COLON;
		case ',':
			return Enum.charType.COMMA;
		case '<':
			return Enum.charType.LT;
		case '=':
			return Enum.charType.EQ;
		case '\'':
			return Enum.charType.CHARC;
		case '\0':
			return Enum.charType.ENDFILE;
		case '{':
			return Enum.charType.LLARPAREN;

		}

		if ((symbol >= 'a' && symbol <= 'z')
				|| (symbol >= 'A' && symbol <= 'Z')) {
			return Enum.charType.LETTER;
		}
		if ((symbol >= '0' && symbol <= '9')) {
			return Enum.charType.DIGIT;
		}
		if (symbol == '\n' || symbol == '\t' || symbol == ' ') {
			return Enum.charType.SPACE;
		}

		return null;
	}

	/**
	 * 得到下一个 token
	 * 
	 * @return
	 */
	public static Node getNextToken(int aa) {
		Node now = new Node();
		char c;
		// 获取下一个非空字符
		c = getNextChar();
		// 读到文件尾
		if (c == '\0')
			return null;
		now.setLine(line);
		now.setRow(row);
		if (c >= '0' && c <= '9') {
			// 无符号整数
			String temp = isNumber(c);
			if (temp != null) {
				now.setData(temp);
				now.setType(Enum.lexType.INTC);
			} else {
				// 标示符不能以数字开始
				now = null;
				Error.setError(line, row, 1);
				Error.printError();
			}
		} else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
			// 标识符或保留字
			String temp = isName(c);
			if (temp != null)
				now.setData(temp);
			else
				now = null;
			now.setType(recognizeName(temp));
		} else if (c == '{') {
			// 注释部分
			int i;
			int num = 1;
			for (i = cur + 1; i < Buffer.length(); i++) {
				if (Buffer.charAt(i) == '{')
					num++;
				else if (Buffer.charAt(i) == '}')
					num--;
				if (Buffer.charAt(i) == '\n') {
					line++;
					row = 1;
				} else if (Buffer.charAt(i) == '\t')
					row += 4;
				else
					row++;
				if (num == 0) // 括号匹配成功
					break;
			}
			if (num != 0) {
				cur = i;
				Error.setError(line, row, 1);
				Error.printError();
			} else
				cur = i + 1;
			now.setData(null);
		} else {
			// 特殊符号
			if (c == ':' && Buffer.charAt(cur + 1) == '='/* 往前读一个字符 */) {
				now.setData(":=");
				now.setType(Enum.lexType.ASSIGN);
				cur += 2;
				row += 2;
			} else if (c == '.' && Buffer.charAt(cur + 1) == '.') {
				now.setData("..");
				now.setType(Enum.lexType.UNDERANGE);
				cur += 2;
				row += 2;
			} else {
				String temp = new String();
				temp += c;
				now.setType(recognizeSymbol(c));
				now.setData(temp);
				if (now.getType() == null) {
					Error.setError(line, row, 1);
					Error.printError();
				}
				cur++;
				row++;
			}
		}
		return now;
	}

	/**
	 * 得到下一个 token
	 * 
	 * @return
	 */
	public static Node getNextToken() {
		Node now = new Node();
		char c;// 读取的当前字符
		int endFlag = Enum.stateType.S12.ordinal();// 结束状态的编号
		String dataStr = "";// 数据信息
		// 得到一个非空字符
		c = getNextChar();
		// 如果读到文件尾
		if (c == '\0') {
			return null;
		}

		now.setLine(line);
		now.setRow(row);
		// 读到字符的类型
		Enum.charType cType = recognizeChar(c);
		if (cType == null) {
			Error.setError(line, row, 1);
			Error.printError();
		}
		
		
		
		// 状态转换
		while (T[curState.ordinal()][cType.ordinal()].ordinal() <= endFlag) {
			row++;
			dataStr += c;
			curState = T[curState.ordinal()][cType.ordinal()];

			if (curState.ordinal() == 6) {
				// 如果为注释状态,就跳出不再读, 防止读到中文 特殊处理
				curState = Enum.stateType.INCOMMENT;
				break;
			}

			c = Buffer.charAt(++cur);
			cType = recognizeChar(c);
			
			if (cType == null) {
				Error.setError(line, row, 1);
				Error.printError();
			}
			
		}
		// 如果不是注释
		if (curState.ordinal() != 17) {
			curState = T[curState.ordinal()][cType.ordinal()];
		}
		switch (curState.ordinal()) {
		case 13:
			// ID或关键字
			now.setData(dataStr);
			now.setType(recognizeName(dataStr));
			break;
		case 14:
			// 无符号整数
			now.setData(dataStr);
			now.setType(Enum.lexType.INTC);
			break;
		case 15:
			// 字符
			now.setData(dataStr);
			now.setType(Enum.lexType.CHARC);
			break;
		case 16:
			// 赋值符号
			now.setData(dataStr);
			now.setType(Enum.lexType.ASSIGN);
			break;
		case 17:
		// 注释处理
		{
			int i;
			int num = 1;
			for (i = cur + 1; i < Buffer.length(); i++) {
				if (Buffer.charAt(i) == '{')
					num++;
				else if (Buffer.charAt(i) == '}')
					num--;
				if (Buffer.charAt(i) == '\n') {
					line++;
					row = 1;
				} else if (Buffer.charAt(i) == '\t')
					row += 4;
				else
					row++;
				if (num == 0) // 括号匹配成功
					break;
			}
			if (num != 0) {
				cur = i;
				Error.setError(line, row, 1);
				Error.printError();
			} else {
				cur = i + 1;
			}
		}
			now.setData(null);
			break;
		case 18:
			// 数组界限..
			now.setData(dataStr);
			now.setType(Enum.lexType.UNDERANGE);
			break;
		case 19:
			// 特殊字符
			now.setData(dataStr);
			now.setType(recognizeSymbol(dataStr.charAt(0)));
			if (now.getType() == null) {
				Error.setError(line, row, 1);
				Error.printError();
			}
			break;
		default:
			Error.setError(line, row, 1);
			Error.printError();
			break;
		}

		curState = Enum.stateType.S0;
		return now;
	}

	/**
	 * 用String传入源文件地址 返回一个token链表
	 * 
	 * @param filePath
	 * @return
	 * @throws Exception
	 */
	public ArrayList<Node> getTokenList(String filePath) throws Exception {
		// 1.文件-->内存缓冲区
		Buffer = readTxt(filePath);
		
		
		
		// 存放结果
		ArrayList<Node> TokenList = new ArrayList<Node>();
		while (true) {
			Node temp = new Node();
			temp = getNextToken();
			// 最后一个Token为EOF
			if (temp == null) {
				Node tmp = new Node();
				tmp.setType(lexType.ENDFILE);
				tmp.setLine(line + 1);
				tmp.setRow(0);
				TokenList.add(tmp);
				break;
			}
			if (temp.getData() == null) // 注释略过
				continue;
			if (Error.getFlag())
				break;
			TokenList.add(temp);
		}
		cur = 0;
		return TokenList;
	}

	public static void main(String[] args) {
		try {
			// 获取工程的路径
			String path = System.getProperty("user.dir");
			path = path.replace("\\", "/");
			// System.out.println(path);

			Scanner scanner = new Scanner();
			ArrayList<Node> tokenList = scanner.getTokenList(path + "/src/c11.snl");
			for (Node node : tokenList) {
				System.out.println(node.getData() + "   " + node.getType());
			}
			
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
